package cn.jtfadmin.base.data.jpa.service;

import cn.jtfadmin.base.data.jpa.utils.TreeUtils;
import cn.jtfadmin.base.lang.common.domain.dto.*;
import cn.jtfadmin.base.lang.common.domain.entity.NamedEntity;
import cn.jtfadmin.base.lang.common.domain.entity.TreeEntity;
import cn.jtfadmin.base.lang.common.utils.AkUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;

/**
 * jpa 树服务
 * @author jtf
 * @since 0.0.1
 * @param <T>
 */
public abstract class JpaTreeService<T extends TreeEntity> extends JpaService<T> implements cn.jtfadmin.base.lang.common.service.BaseTreeService<T> {


    @Autowired
    protected EntityManager em;


    protected String findParentId(Object object){
        Field parentIdField = ReflectionUtils.findField(object.getClass(), "parentId");
        if (Objects.isNull(parentIdField)) {
            throw new RuntimeException("未找到字段parentId");
        }
        parentIdField.setAccessible(true);

        try {
            return (String) parentIdField.get(object);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * 实际新增
     * @param addPDTO 参数
     * @return 结果
     */
    @Transactional
    protected   T doAdd(Object addPDTO){
        T t = AkUtils.copyProperties(addPDTO, getEntityClass());
        String parentId = findParentId(addPDTO);
        if (StringUtils.hasText(parentId)) {
            T parent = findRequiredById(parentId);
            String parentIds = parent.getParentIds();
            if (StringUtils.hasText(parentIds)) {
                t.setParentIds(parentIds + parentId + ",");
            } else {
                t.setParentIds("," + parentId + ",");
            }
        }
        return save(t);
    }



    /**
     * 实际新增
     * @param editPDTO 编辑参数
     * @param <V> 编辑参数类型
     * @return 编辑后结果
     */
    @Transactional
    protected  <V extends EditPDTO> T doEdit(V editPDTO){
        String id = editPDTO.getId();
        T entity = findRequiredById(id);

        String newParentId = findParentId(editPDTO);
        String oldParentId = entity.getParentId();
        boolean parentChange = true;
        boolean s1has = StringUtils.hasText(oldParentId);
        boolean s2has = StringUtils.hasText(newParentId);
        if (s1has || s2has) {
            parentChange = !Objects.equals(newParentId, oldParentId);
        }
        if (parentChange) {
            List<T> allChildren = findAllChildren(id, false);
            if (StringUtils.hasText(newParentId)) {
                isCircle(entity, newParentId);
                T parent = findRequiredById(newParentId);
                String parentIds = parent.getParentIds();
                if (StringUtils.hasText(parentIds)) {
                    entity.setParentIds(parentIds + newParentId + ",");
                } else {
                    entity.setParentIds("," + newParentId + ",");
                }
                for (T allChild : allChildren) {
                    String childParentIds = allChild.getParentIds();
                    int index = parentIds.indexOf("," + id + ",");
                    allChild.setParentIds(entity.getParentIds() + childParentIds.substring(index + 1));
                }
            } else {
                for (T allChild : allChildren) {
                    String parentIds = allChild.getParentIds();
                    int index = parentIds.indexOf("," + id + ",");
                    allChild.setParentIds(parentIds.substring(index));
                }
            }
            if (!CollectionUtils.isEmpty(allChildren)) {
                for (T allChild : allChildren) {
                    save(allChild);
                }
            }
        }

        AkUtils.copyProperties(editPDTO, entity, "parentIds");
        return save(entity);

    }


    protected void isCircle(T entity, String parentId) {
        if (entity.getId().equals(parentId)) {
            throw new RuntimeException("循环引用");
        }
        List<T> childrenList = findAllChildren(entity.getId(), false);
        for (T child : childrenList) {
            if (child.getId().equals(parentId)) {
                throw new RuntimeException("循环引用");
            }
        }
    }

    @Override
    @Transactional
    public void deleteBatch(@NotNull Collection<String> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return;
        }
        List<T> list = findByIds(ids);
        List<T> delete = new LinkedList<>();
        Set<String> deleteIds = new HashSet<>();
        Iterator<T> iterator = list.iterator();
        while (iterator.hasNext()){
            T next = iterator.next();
            List<T> allChildren = findAllChildren(next.getId(), false);
            for (T allChild : allChildren) {
                if(deleteIds.add(allChild.getId())){
                    delete.add(allChild);
                }
            }
            if(deleteIds.add(next.getId())){
                delete.add(next);
            }
        }
        if(!CollectionUtils.isEmpty(delete)){
            getJpaRepository().deleteAll(delete);
        }
    }



    /**
     * 获取children
     * @param id id
     * @param filterUserPermFlag 是否过滤用户
     * @return
     */
    public List<T> findChildren(@NotBlank String id, boolean filterUserPermFlag){
        if(filterUserPermFlag){
            return findAll(((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("parentId"), id)), Sort.unsorted());
        }
        CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
        Class<T> entityClass = getEntityClass();
        CriteriaQuery<T> query = criteriaBuilder.createQuery(entityClass);
        Root<T> root = query.from(entityClass);
        query.where(criteriaBuilder.equal(root.get("parentId"), id));
        return em.createQuery(query).getResultList();
    }

    /**
     * 获取所有的children
     * @param filterUserPermFlag 是否过滤用户
     * @param id id
     * @return
     */
    public List<T> findAllChildren(@NotBlank String id, boolean filterUserPermFlag){
        if(filterUserPermFlag){
            return findAll(((root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("parentIds"), "%,"+id+",%")), Sort.unsorted());
        }
        CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
        Class<T> entityClass = getEntityClass();
        CriteriaQuery<T> query = criteriaBuilder.createQuery(entityClass);
        Root<T> root = query.from(entityClass);
        query.where(criteriaBuilder.like(root.get("parentIds"), "%,"+id+",%"));
        return em.createQuery(query).getResultList();
    }




    /**
     * 前端查询查询
     * @param specPDTO 查询条件
     * @param modelClass 返回结果
     * @return 查询结果
     */
    public <V> List<TreeRDTO<V>> findTree(@NotNull SpecPDTO<T> specPDTO, @NotNull Class<V> modelClass){
       return TreeUtils.buildTreeRDTO(modelClass, findAll(specPDTO));
    }

    /**
     * 前端查询查询
     * @param column 返回字段，如果为空默认id， 或named
     * @return 查询结果
     */
    public List<AntdvTreeDataRDTO> findAntdTree(String column){
        if(!StringUtils.hasText(column)){
            if(NamedEntity.class.isAssignableFrom(getEntityClass())){
                column = "name";
            }else{
                column = "id";
            }
        }
        return TreeUtils.buildAntdvTreeRDTO(findAll((Specification<T>)null, Sort.unsorted()), null, StringUtils.commaDelimitedListToStringArray(column));
    }


    @Override
    public List<AntdvTreeDataRDTO> findAntdTree(String columns, SpecPDTO<T> specPDTO) {
        return TreeUtils.buildAntdvTreeRDTO(findAll(specPDTO), null, StringUtils.commaDelimitedListToStringArray(columns));
    }

    @Override
    protected Class<? extends BaseTreeExcelModalDTO> getExcelClass() {
        throw new IllegalArgumentException("导出excel类型未配置");
    }


    @Transactional
    protected <V extends BaseExcelModalDTO> List<T> excelDTOToEntity(List<V> list)
    {

        String idField = getExcelDTOToEntityIdField();
        if(!StringUtils.hasText(idField)){
            return super.excelDTOToEntity(list);
        }
        List<T> result = new ArrayList<>(list.size());
        for (int i = 0; i < list.size(); i++) {
            V v = list.get(i);
            if(!(v instanceof BaseTreeExcelModalDTO)){
                throw new IllegalArgumentException("excel类型错误");
            }
            List<T> entityList = findByField(idField, cn.jtfadmin.base.lang.common.utils.ReflectionUtils.getFieldValue(v, idField));
            if(CollectionUtils.isEmpty(entityList)){
                T t = AkUtils.copyProperties(v, getEntityClass());
                excelFillEmbedId(v, t);
                Object pidName = ((BaseTreeExcelModalDTO) v).getPidName();
                if(Objects.isNull(pidName)){
                    throw new IllegalArgumentException("第" + v.getRowNum() + "条数据错误,上级数据为空");
                }
                List<T> parents = findByField(idField, pidName);
                T parent = null;
                if(CollectionUtils.isEmpty(parents)){
                    throw new IllegalArgumentException("第" + v.getRowNum() + "条数据错误,上级未找到");
                }else if(parents.size() == 1){
                    parent = parents.iterator().next();
                }else {
                    throw new IllegalArgumentException("第" + v.getRowNum() + "条数据错误,上级找到"+parents.size()+"条");
                }
                String parentId = parent.getId();
                String parentIds = parent.getParentIds();
                if (StringUtils.hasText(parentIds)) {
                    t.setParentIds(parentIds + parentId + ",");
                } else {
                    t.setParentIds("," + parentId + ",");
                }
                result.add(save(t));
            }else if(entityList.size() == 1){
                T next = entityList.iterator().next();
                T parent = null;
                Object pidName = ((BaseTreeExcelModalDTO) v).getPidName();
                if(Objects.nonNull(pidName)){
                    List<T> parents = findByField(idField, pidName);
                    if(CollectionUtils.isEmpty(parents)){
                        throw new IllegalArgumentException("第" + v.getRowNum() + "条数据错误,上级未找到");
                    }else if(parents.size() == 1){
                        parent = parents.iterator().next();
                    }else {
                        throw new IllegalArgumentException("第" + v.getRowNum() + "条数据错误,上级找到"+parents.size()+"条");
                    }
                }
                AkUtils.copyProperties(v, next, "parentId", "parentIds");
                excelFillEmbedId(v, next);
                String newParentId = parent.getId();
                String oldParentId = next.getParentId();
                boolean parentChange = true;
                boolean s1has = StringUtils.hasText(oldParentId);
                boolean s2has = StringUtils.hasText(newParentId);
                if (s1has || s2has) {
                    parentChange = !Objects.equals(newParentId, oldParentId);
                }
                String id = next.getId();
                if (parentChange) {
                    List<T> allChildren = findAllChildren(id, false);
                    if (StringUtils.hasText(newParentId)) {
                        isCircle(next, newParentId);
                        String parentIds = parent.getParentIds();
                        if (StringUtils.hasText(parentIds)) {
                            next.setParentIds(parentIds + newParentId + ",");
                        } else {
                            next.setParentIds("," + newParentId + ",");
                        }
                        for (T allChild : allChildren) {
                            String childParentIds = allChild.getParentIds();
                            int index = parentIds.indexOf("," + id + ",");
                            allChild.setParentIds(next.getParentIds() + childParentIds.substring(index + 1));
                        }
                    } else {
                        for (T allChild : allChildren) {
                            String parentIds = allChild.getParentIds();
                            int index = parentIds.indexOf("," + id + ",");
                            allChild.setParentIds(parentIds.substring(index));
                        }
                    }
                    if (!CollectionUtils.isEmpty(allChildren)) {
                        for (T allChild : allChildren) {
                            save(allChild);
                        }
                    }
                }

                result.add(next);
            }else {
                throw new IllegalArgumentException("第" + v.getRowNum() + "条数据错误,系统存在"+entityList.size()+"条");
            }

        }
        return result;


    }

}
